﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Etest.Models;
using RestSharp;
using System.Xml.Serialization;
using System.Xml;

namespace Etest.Controllers
{
    public class TestController : Controller
    {
        etestEntities db = new etestEntities("name=etestEntitiesNadvor");
 
        //
        // GET: /Test/

        public ActionResult Index()
        {
            //take all courses that this user is enrolled in
            //User.Identity.Name is null
            //var userCourses = db.UserOnCourse.Where(m => m.Users.UserName == User.Identity.Name);

            //ViewBag.courseId = new SelectList(userCourses.ToList(), "id", "userId");
            var userCourses = db.Course.Where(m => m.UserOnCourse.Where(q => q.Users.UserName == User.Identity.Name).Any());
            ViewBag.courseId = new SelectList(userCourses.ToList(), "id", "name");
            return View();
        }
        public PartialViewResult TestsPartial(int course)
        {
            
            
            var userTests = db.Test
                              .Include(s => s.Users)
                              .Where(m => m.courseId == course)
                              .ToList();

            var evaluatedTests = db.GeneratedTest
                                   .Where(m => m.Test.courseId == course && m.Users.UserName == User.Identity.Name && m.test_score != (int?)null);

            
           
            //did user press start test (is there generated test)
            //if tests already evaluated or time is up don't show them
        
            return PartialView(userTests.Except(evaluatedTests.Select<GeneratedTest, Test>(s => s.Test)).ToList());
       
        }

        //imaat isti parametri so SolveTest, nema potreba od SolveTest
        public ActionResult NextQuestion(int generatedTestId, int qIdIndex)
        {
            //moze da proveruvas dali voopste pratil parametri

            if (db.GeneratedTest.Where(m => m.id == generatedTestId && m.Users.UserName == User.Identity.Name).Any())
            {
                QuestionViewModel qvm = new QuestionViewModel();
                qvm.options = new List<QOption>();

                //join Question and GeneratedTestQuestion to get all questions from generatedTestId
                //For every question question.GeneratedQuestion <=> m.GeneratedQuestion
                //db.Question odi po red i taka gi stava vo lista od 1,2,3
                //sekogas kje ima ist redosled
               
                //qvm.question = db.Question
                //    .Where(m => m.GeneratedTestQuestion.Where(s => s.gentestId == generatedTestId).Any())
                //    .ToList()[qIdIndex];

                var allGeneratedQuestions = db.GeneratedTestQuestion.Where(m => m.gentestId == generatedTestId).ToList();
                var query = allGeneratedQuestions
                            .Join(db.Question,
                            gq => gq.questionId,
                            q => q.id,
                            (gq, q) => new { GeneratedTestQuestion = gq, Question = q })
                            .Select(x => x.Question);
                qvm.question = query.ToList()[qIdIndex];
                //find all options to the question above
                qvm.options = db.QOption.Where(s => s.QuestionOption.Where(mu => mu.questionId == qvm.question.id).Any()).ToList();

                //find all already selected options (if user continues test) for this question
                List<UserAnswer> alreadySelected = db.UserAnswer.Where(m => m.generatedTestId == generatedTestId && m.questionId == qvm.question.id).ToList();

                //check if no such
                if (alreadySelected.Count == 0)
                {
                    qvm.selectedOptions = new List<bool>();
                    for (int i = 0; i < qvm.options.Count; i++)
                    {
                        qvm.selectedOptions.Add(false);
                    }
                }
                else
                {
                    if (qvm.options[0].checkbox_or_ratio == true)
                    {
                        qvm.selectedOptions = new List<bool>();
                        for (int i = 0; i < qvm.options.Count; i++)
                        {
                            qvm.selectedOptions.Add(alreadySelected[i].option_value);
                        }
                    }
                    else
                    {
                        //find selected radio buttion if exists
                        //maybe user did not selected and clicked Next
                        try
                        {
                            var selectedRadio = alreadySelected.Where(m => m.option_value == true).Select<UserAnswer, int>(x => alreadySelected.IndexOf(x)).Single();
                            qvm.selected = selectedRadio.ToString();
                        }
                        catch (Exception e)
                        {
                            qvm.selected = "-1";
                        }
                            

                        
                    }
                }
                
                qvm.generatedId = generatedTestId;
                //vidi slucaj koga e kraj na testot
                qvm.qId = qIdIndex;

                //This part is for marking already answered questions
                //with red and green circles
                //
                //

                //var questions = db.Question.Where(m => m.GeneratedTestQuestion.Where(q => q.gentestId == generatedTestId).Any()).ToList();
                //ViewBag.generatedtest = generatedTestId;
                //ViewBag.numberOfQuestions = questions.Count;
                //ViewBag.startQuestion = startQuestion;

                //every generated test is assigned to one User, the check is above
                    //ne kaj useranswer mora da stavis i od koj test ide
                var answeredQuestions = db.GeneratedTestQuestion
                    .Where(m => m.gentestId == generatedTestId && m.Question.UserAnswer.Where(s =>s.generatedTestId==generatedTestId && s.option_value == true).Any())
                    .ToList();

                //mark all already answered questions from all generated questions
                //ovde bi trebalo i bez OrderBy da raboti oti gi zema po red generated questions
                //var allGeneratedQuestions = db.GeneratedTestQuestion.Where(m => m.gentestId == generatedTestId).OrderBy(oo=>oo.id).ToList();

                //get the indices, if not answered -1
                var questionIndices = allGeneratedQuestions.Select<GeneratedTestQuestion, int>(x => answeredQuestions.IndexOf(x)).ToList();
                //var questionIndices = questions.Select<Question, int>(x => answeredQuestions.IndexOf(x)).ToList();
                qvm.markedQuesionIndices = questionIndices;

                return View(qvm);
            }
            return RedirectToAction("Index");
        }
        
        [HttpPost]
        public ActionResult NextQuestion(QuestionViewModel qvm)
        {

            //proveri ednas ako ednas odgovoril
            if (ModelState.IsValid)
            {
                //check if question is for this user
                if (db.GeneratedTest.Where(m => m.id == qvm.generatedId && m.Users.UserName == User.Identity.Name).Any())
                {
                    //praka referenca
                    //here OrderBy is not neccessary, returns them by UserAnswer.id
                    var originalAnswers = db.UserAnswer.Where(m => m.questionId == qvm.question.id && m.generatedTestId == qvm.generatedId).ToList();

                    UserAnswer useranswer;

                    for (int i = 0; i < qvm.options.Count; i++)
                    {
                        useranswer = new UserAnswer();
                        useranswer.generatedTestId = qvm.generatedId;
                        useranswer.questionId = qvm.question.id;
                        useranswer.questionOptId = qvm.options[i].id;
                        //if checkbox else radiobutton
                        if (qvm.selectedOptions != null) useranswer.option_value = qvm.selectedOptions[i];
                        else useranswer.option_value = (qvm.selected == i.ToString()) ? true : false;

                        //ako niednas ne odgovoril
                        if (originalAnswers.Count == 0) db.UserAnswer.Add(useranswer);
                        else
                        {
                            //ako odgovoril veke samo updajtuvaj
                            useranswer.id = originalAnswers[i].id;
                            db.Entry(originalAnswers[i]).CurrentValues.SetValues(useranswer);
                            //db.SaveChanges();
                        }

                    }
                    db.SaveChanges();

                    //If user clicks next on last question
                    if (qvm.qId + 1 >= qvm.markedQuesionIndices.Count)
                    {
                        //find first unmarked question (if exists) and redirect user to solve it
                        for (int i = 0; i < qvm.markedQuesionIndices.Count; i++)
                        {
                            if (qvm.markedQuesionIndices[i] == -1)
                            {
                                return RedirectToAction("NextQuestion", new { generatedTestId = qvm.generatedId, qIdIndex = i });
                            }
                        }

                        //if you came to end and no such question exists just return same question and do nothing
                        return RedirectToAction("NextQuestion", new { generatedTestId = qvm.generatedId, qIdIndex = qvm.qId });
                    }
                    return RedirectToAction("NextQuestion", new { generatedTestId = qvm.generatedId, qIdIndex = qvm.qId + 1 });
                }

                //if non-authorized access
                return RedirectToAction("Index");
                
            }



            return View(qvm);
            //ako ima veke odgovoreno prati mu edit template
            //return RedirectToAction("NextQuestion", "Test", new { generatedTestId = qvm.generatedId, qIdIndex = qvm.qId + 1 });
            //return RedirectToAction("SolveTest", new { generatedtest = qvm.generatedId, startQuestion = qvm.qId + 1 });
        }

    
        [HttpPost]
        //moze ovde samo test.id da se prefrla
        public ActionResult GenerateTest(Test test)
        {
            //checks if test is generated for the logged user
            GeneratedTest generatedtest = db.GeneratedTest.Where(m => m.testId == test.id && m.Users.UserName == User.Identity.Name).FirstOrDefault();
            
            //if test is not generated for this user
            if (generatedtest == null)
            {
                generatedtest = new GeneratedTest();
                generatedtest.testId = test.id;
                generatedtest.userId = db.Users.Where(m => m.UserName == User.Identity.Name).First().UserId;

                var questions = db.Question.Where(m => m.TestQuestion.Where(q => q.testId == test.id).Any()).ToList();
                var r = new Random();

                var generatedQuestions = new List<Question>();
                int randomNumber;

                for (int i = 0; i < test.number_of_questions && questions.Count!=0; i++)
                {

                    GeneratedTestQuestion generatedtestquestion = new GeneratedTestQuestion();
                    generatedtestquestion.gentestId = test.id;
                    randomNumber = r.Next(questions.Count);
                    generatedtestquestion.questionId = questions[randomNumber].id;
                    //remove selected question from list so no repetition
                    //Count automatically is reduced to one and elements shifted
                    questions.Remove(questions[randomNumber]);
                    generatedtest.GeneratedTestQuestion.Add(generatedtestquestion);
                }
                db.GeneratedTest.Add(generatedtest);
                db.SaveChanges();
            }

            //check if this test is for this user (no non-authorized access)
            //if (generatedtest.Users.UserName == User.Identity.Name
            //in NextQuestion is made that check no need to do it again

            //else if test is already generated just continue solving it
                //redirect to action
            return RedirectToAction("NextQuestion", "Test", new { generatedTestId = generatedtest.id, qIdIndex = 0 }); //solve test
        }

        
        public ActionResult EvaluateTest(int generatedTestId)
        {
           
            //let professor only evaluate more times (no changes are made)
            //even if professor can evaluate he can't alter test_score
            //only allow professors assigned on that course to evaluate (or professors that generated that test)
            
            if (User.IsInRole("Professor"))
            {
                //check if professor generated that test
                var profTest = db.Test
                                 .Where(m => m.Users.UserName == User.Identity.Name && m.GeneratedTest.Where(s => s.id == generatedTestId).Any())
                                 .FirstOrDefault();
                if (profTest != null)
                {
                    EvaluateTestViewModel etvm = new EvaluateTestViewModel();
                    etvm.correctAnswers = new List<Question>();
                    etvm.correctAnswers = db.Question.Where(m => m.GeneratedTestQuestion.Where(s => s.gentestId == generatedTestId).Any())
                                                     .Include(s => s.QuestionOption)
                                                     .ToList();
                    
                    //do not put test_score for professor
                    etvm.userAnswers = db.UserAnswer.Where(m => m.generatedTestId == generatedTestId).ToList();
                       
                
                return View(etvm);
                }
            }

            
            //ensure no non-authorized access
            var generatedTest = db.GeneratedTest.Where(m => m.id == generatedTestId && m.Users.UserName == User.Identity.Name).FirstOrDefault();
            if (generatedTest!=null)
            {
                //check if test already evaluated
                if (generatedTest.test_score == (int?)null)
                {
                    EvaluateTestViewModel etvm = new EvaluateTestViewModel();
                    etvm.correctAnswers = new List<Question>();
                    etvm.correctAnswers = db.Question.Where(m => m.GeneratedTestQuestion.Where(s => s.gentestId == generatedTestId).Any()).Include(s => s.QuestionOption).ToList();

                    etvm.userAnswers = db.UserAnswer.Where(m => m.generatedTestId == generatedTestId).ToList();

                 


                    generatedTest.test_score = 0;

                    //you can calculate score based on number of correct options here
                    //for the moment just simple check if all are correct
                    int countCorrect;
                    List<UserAnswer> userAnswers;
                    foreach (var question in etvm.correctAnswers)
                    {
                        countCorrect = 0;
                        userAnswers = question.UserAnswer.Where(s => s.generatedTestId == generatedTestId).ToList();
                        foreach (var useranswer in userAnswers)
                        {
                            if (useranswer.option_value == question.QuestionOption.Where(m => m.questionId == question.id && m.optionId == useranswer.questionOptId).FirstOrDefault().is_option_correct)
                            {
                                countCorrect++;
                            }
                        }
                        if (countCorrect == question.QuestionOption.Count())
                        {
                            generatedTest.test_score++;
                            
                        }
                    }
                    db.Entry(generatedTest).State = EntityState.Modified;
                    db.SaveChanges();
                    //func<Tsource,bool>
                    //za sekoe prasanje odi po red

                    //for each question and for all user answers of that question need to be correct

                    //include unanswered questions
                    // etvm.correctAnswers = db.Question.Select<Question, Question>(s => userAnswers.Find(q => q.id == s.id)).ToList();
                    // etvm.userAnswers = userAnswers;                      

                    return View(etvm);
                }
                //test already evaluated
                return RedirectToAction("Index");

                //if Evaluated don't to allow to test again disable Test button on /Test
            }
            //if non-authorized access redirect to /Test
            return RedirectToAction("Index");
            
        }
        public ActionResult getAutorizationToken()
        {
            
            var client = new RestClient("http://10.10.10.2:5000/v2.0/tokens");
            // client.Authenticator = new HttpBasicAuthenticator(username, password);

            var request = new RestRequest(Method.POST);
    
            //request.AddUrlSegment("id", 123); // replaces matching token in request.Resource
            request.AddParameter("application/json", "{\"auth\":{\"passwordCredentials\":{\"username\":\"admin\", \"password\":\"stack\"}, \"tenantId\":\"e594679e284849dcb03a709eec58841a\"}}", ParameterType.RequestBody);
            // easily add HTTP Headers
            request.AddHeader("Content-Type", "application/json");
            request.RequestFormat = DataFormat.Json;
            
            // add files to upload (works with compatible verbs)
            //request.AddFile(path);

            // execute the request
            var response = client.Execute(request);
            var content = response.Content; // raw content as string
            

            
            ViewBag.content = content;


            return View();
        }

        public ActionResult Deserialize()
        {
            XmlSerializer ser = new XmlSerializer(typeof(Access));
            Access xmlRootTag;
            string path = HttpContext.ApplicationInstance.Server.MapPath("~/App_Data/") + "XMLResponse.xml";
            using (XmlReader reader = XmlReader.Create(path))
            {
                xmlRootTag = (Access)ser.Deserialize(reader);
            }

            return View();
        }

    }
}
